#include "HikCamera.hpp"

namespace qd::Device {

HikCamera::HikCamera(): queue_(1), camera_handle_(nullptr), running_(true) {
    this->params_ = Parameters();
    params_.exposure_time = 1000.0;
    params_.gain = 16.0;
    params_.frame_rate = 250.0;
    params_.enable_frame_rate = true;
    params_.adc_bit_depth = "ADCBitDepth_8";
    params_.pixel_format = "BayerRG8";
    params_.enable_DeviceSerialNumber = false;
    params_.device_serial_number = "";
    params_.cvt_quality = 1;

    // 创建句柄
    this->camera_handle_ = get_camera_handle(params_);
    // 设置相机参数
    set_hik_param(this->camera_handle_, this->params_);
    // 开始取流
    nRet = MV_CC_StartGrabbing(camera_handle_);

    this->daemon_thread_ = std::thread(&HikCamera::image_callback, this);
}

HikCamera::~HikCamera() {
    running_ = false;
    if (camera_handle_ != nullptr) {
        MV_CC_StopGrabbing(camera_handle_);
        MV_CC_CloseDevice(camera_handle_);
        MV_CC_DestroyHandle(&camera_handle_);
    }
    if (daemon_thread_.joinable()) {
        daemon_thread_.join();
    }
}

void HikCamera::image_callback() {

    while (running_) {
        // 获得图像缓存
        nRet = MV_CC_GetImageBuffer(camera_handle_, &out_frame_buffer_, 1000);
        if (nRet != MV_OK) {
            printf("No image data! nRet [0x%x]\n", nRet);
            continue;
        }

        auto img_time = get_time_();
        // 存储数据进队列
        cv::Mat dst_img(
            cv::Size(out_frame_buffer_.stFrameInfo.nWidth, out_frame_buffer_.stFrameInfo.nHeight),
            CV_8UC3
        );
        // 像素格式转换
        convert_param.nWidth = out_frame_buffer_.stFrameInfo.nWidth;
        convert_param.nHeight = out_frame_buffer_.stFrameInfo.nHeight;
        // 输入src
        convert_param.enSrcPixelType = out_frame_buffer_.stFrameInfo.enPixelType;
        convert_param.pSrcData = out_frame_buffer_.pBufAddr;
        convert_param.nSrcDataLen = out_frame_buffer_.stFrameInfo.nFrameLen;
        // 输出dst
        convert_param.enDstPixelType = PixelType_Gvsp_BGR8_Packed;
        convert_param.pDstBuffer = dst_img.data; // 输出数据缓冲区
        convert_param.nDstBufferSize =
            out_frame_buffer_.stFrameInfo.nExtendWidth * out_frame_buffer_.stFrameInfo.nExtendHeight * 4 + 2048;
        // 执行转换
        nRet = MV_CC_ConvertPixelType(camera_handle_, &convert_param);

        
        queue_.push({ dst_img, img_time });

        // 释放图像缓存
        MV_CC_FreeImageBuffer(camera_handle_, &out_frame_buffer_);
    }
}

cv::Mat HikCamera::get_cv_img() {
    return this->queue_.pop().img;
}

CameraData HikCamera::get_camera_queue(){
    return this->queue_.pop();
}

void* HikCamera::get_camera_handle(const Parameters& params) {
    void* handle;
    MVCC_STRINGVALUE string_value;

    // 枚举设备
    MV_CC_DEVICE_INFO_LIST device_list;
    nRet = MV_CC_EnumDevices(MV_USB_DEVICE, &device_list);

    // 等待设备连接
    while (device_list.nDeviceNum == 0) {
        printf("No camera found! nRet [0x%x]\n", nRet);
        std::this_thread::sleep_for(std::chrono::milliseconds(500));
        nRet = MV_CC_EnumDevices(MV_USB_DEVICE, &device_list);
    }

    // 查找序列号
    MV_CC_DEVICE_INFO* device_info = device_list.pDeviceInfo[0];
    for (auto* device: device_list.pDeviceInfo) {
        if (!params.enable_DeviceSerialNumber) {
            break;
        }

        // 打开设备
        nRet = MV_CC_CreateHandle(&handle, device);
        if (nRet != MV_OK) {
            printf("CreateHandle failed! nRet [0x%x]\n", nRet);
            continue;
        }
        nRet = MV_CC_OpenDevice(handle);
        if (nRet != MV_OK) {
            printf("OpenDevice failed! nRet [0x%x]\n", nRet);
            continue;
        }

        // 获取序列号
        MV_CC_GetStringValue(handle, "DeviceSerialNumber", &string_value);
        std::cout << "Found DeviceSerialNumber: " << string_value.chCurValue << std::endl;

        // 比较序列号
        if (string_value.chCurValue == params.device_serial_number.c_str()) {
            device_info = device;
        }
        MV_CC_CloseDevice(handle);
    }

    // 打开设备
    nRet = MV_CC_CreateHandle(&handle, device_info);
    printf("MV_CC_CreateHandle! nRet [0x%x]\n", nRet);
    nRet = MV_CC_OpenDevice(handle);
    printf("MV_CC_OpenDevice! nRet [0x%x]\n", nRet);
    MV_CC_GetStringValue(handle, "DeviceSerialNumber", &string_value);
    std::cout << "Select DeviceSerialNumber: " << string_value.chCurValue << std::endl;

    return handle;
}

void HikCamera::set_hik_param(void* handle, const Parameters& params) {
    // 锁帧
    nRet = MV_CC_SetBoolValue(handle, "AcquisitionFrameRateEnable", params.enable_frame_rate);
    printf("Set AcquisitionFrameRateEnable! nRet [0x%x]\n", nRet);
    // 帧率限制
    nRet = MV_CC_SetFloatValue(handle, "AcquisitionFrameRate", params.frame_rate);
    printf("Set AcquisitionFrameRate! nRet [0x%x]\n", nRet);
    // 曝光
    nRet = MV_CC_SetFloatValue(camera_handle_, "ExposureTime", params.exposure_time);
    printf("Set ExposureTime! nRet [0x%x]\n", nRet);
    // 增益
    nRet = MV_CC_SetFloatValue(camera_handle_, "Gain", params.gain);
    printf("Set Gain! nRet [0x%x]\n", nRet);
    // 像素格式
    nRet = MV_CC_SetEnumValueByString(camera_handle_, "PixelFormat", params.pixel_format.c_str());
    printf("Set PixelFormat! nRet [0x%x]\n", nRet);
    // ADC比特深度
    nRet = MV_CC_SetEnumValueByString(camera_handle_, "ADCBitDepth", params.adc_bit_depth.c_str());
    printf("Set ADCBitDepth! nRet [0x%x]\n", nRet);
    //设置插值方法
    nRet = MV_CC_SetBayerCvtQuality(camera_handle_, 1);
    printf("Set BayerCvtQuality! nRet [0x%x]\n", nRet);
}

} // namespace qd::Device
